package dimension.starry.block.machine.industry;

import dimension.starry.StarryDimension;
import dimension.starry.block.machine.industry.ArmorTable.ArmorTable;
import dimension.starry.block.machine.industry.ArmorTable.ArmorTableContainer;
import dimension.starry.block.machine.industry.ArmorTable.ArmorTableScreen;
import dimension.starry.block.machine.industry.ArmorTable.ArmorTableTileEntity;
import dimension.starry.block.machine.industry.crusher.Crusher;
import dimension.starry.block.machine.industry.crusher.CrusherContainer;
import dimension.starry.block.machine.industry.crusher.CrusherScreen;
import dimension.starry.block.machine.industry.crusher.CrusherTileEntity;
import dimension.starry.block.machine.industry.rollingMill.RollingMill;
import dimension.starry.block.machine.industry.rollingMill.RollingMillContainer;
import dimension.starry.block.machine.industry.rollingMill.RollingMillScreen;
import dimension.starry.block.machine.industry.rollingMill.RollingMillTileEntity;
import dimension.starry.block.machine.industry.smallLathe.*;
import dimension.starry.block.machine.industry.steamer.*;
import dimension.starry.block.machine.simple.SimpleMachineRegister;
import dimension.starry.block.machine.simple.castTable.CastTableContainer;
import dimension.starry.block.typeInterface.MachineContainer;
import dimension.starry.block.typeInterface.MachineScreen;
import dimension.starry.block.typeInterface.MachineTileEntity;
import dimension.starry.item.ItemGroups;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
import net.minecraft.block.Block;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.gui.screen.ingame.HandledScreens;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.util.Identifier;

import java.util.Arrays;

/**
 * @author 150149
 */
public enum IndustryMachineRegister {
    STEAMER_ENTRY("steamer_entry", new SteamerEntry(), SteamTileEntity::new,
        (int syncId, PlayerInventory inv, PacketByteBuf data) -> new SteamerContainer(ScreenHandlerType.GENERIC_3X3,
            syncId, inv, data.readBlockPos()
        ), SteamerScreen::new
    ),
    STEAMER_BACK("steamer_back", new SteamerBack(), null, null, null),
    STEAMER_TANK_FRONT("steamer_tank_front", new SteamerTankFront(), null, null, null),
    STEAMER_TANK_BACK("steamer_tank_back", new SteamerTankBack(), null, null, null),
    STEAMER_FLYWHEEL("steamer_flywheel", new SteamerFlyWheel(), null, null, null),
    STEAMER_STORE("steamer_store", new SteamerStore(), null, null, null),
    STEAMER_BASE("steamer_base", new SteamerBase(), null, null, null),
    CRUSHER("crusher", new Crusher(), CrusherTileEntity::new,
        (int syncId, PlayerInventory inv, PacketByteBuf data) -> new CrusherContainer(ScreenHandlerType.GENERIC_3X3,
            syncId, inv, data.readBlockPos()
        ), CrusherScreen::new
    ),
    ROLLING_MILL("rolling_mill", new RollingMill(), RollingMillTileEntity::new,
        (int syncId, PlayerInventory inv, PacketByteBuf data) -> new RollingMillContainer(ScreenHandlerType.GENERIC_3X3,
            syncId, inv, data.readBlockPos()
        ), RollingMillScreen::new
    ),
    SMALL_LATHE_LEFT(
        "small_lathe_left", new SmallLatheLeft(), SmallLatheTileEntity::new,
        (int syncId, PlayerInventory inv, PacketByteBuf data) -> new SmallLatheContainer(ScreenHandlerType.GENERIC_3X3,
            syncId, inv, data.readBlockPos()
        ), SmallLatheScreen::new
    ),
    SMALL_LATHE_RIGHT("small_lathe_right", new SmallLatheRight(), null, null, null),
    ARMOR_TABLE(
        "armor_table", new ArmorTable(), ArmorTableTileEntity::new,
        (int syncId, PlayerInventory inv, PacketByteBuf data) -> new ArmorTableContainer(ScreenHandlerType.GENERIC_3X3,
            syncId, inv, data.readBlockPos()
        ), ArmorTableScreen::new
    ),
    ;

    private final String name;
    private final BlockItem item;
    private final Block block;
    private final FabricBlockEntityTypeBuilder.Factory<? extends MachineTileEntity> entityFactory;
    private final ExtendedScreenHandlerType.ExtendedFactory<MachineContainer> containerFactory;
    private BlockEntityType<? extends BlockEntity> blockEntityType;
    private final HandledScreens.Provider<MachineContainer, MachineScreen<MachineContainer>> screenFactory;
    private ExtendedScreenHandlerType<? extends MachineContainer> screenHandlerType;

    IndustryMachineRegister(
        String name, Block block,
        FabricBlockEntityTypeBuilder.Factory<? extends MachineTileEntity> entityFactory,
        ExtendedScreenHandlerType.ExtendedFactory<MachineContainer> containerFactory,
        HandledScreens.Provider<MachineContainer, MachineScreen<MachineContainer>> screenFactory
    ) {
        this.name = name;
        this.item = new BlockItem(block, new FabricItemSettings());
        this.block = block;
        this.entityFactory = entityFactory;
        this.containerFactory = containerFactory;
        this.screenFactory = screenFactory;
    }

    public static void onInitialize() {
        Arrays.stream(IndustryMachineRegister.values())
            .peek(machineEnum -> {
                Registry.register(
                    Registries.BLOCK, new Identifier(StarryDimension.MOD_ID, machineEnum.getName()),
                    machineEnum.getBlock()
                );
                Registry.register(
                    Registries.ITEM, new Identifier(StarryDimension.MOD_ID, machineEnum.getName()),
                    machineEnum.getItem()
                );
                ItemGroups.registerToGroup(machineEnum.getItem());
            })
            .filter(machineEnum -> machineEnum.getEntityFactory() != null)
            .forEach(machineEnum -> {
                machineEnum.blockEntityType = Registry.register(Registries.BLOCK_ENTITY_TYPE,
                    new Identifier(StarryDimension.MOD_ID, machineEnum.getName()),
                    FabricBlockEntityTypeBuilder.create(machineEnum.getEntityFactory(), machineEnum.getBlock())
                        .build(null)
                );
                machineEnum.screenHandlerType = Registry.register(
                    Registries.SCREEN_HANDLER,
                    new Identifier(
                        StarryDimension.MOD_ID,
                        machineEnum.getName()
                    ),
                    new ExtendedScreenHandlerType<>(
                        (syncId, playerInventory, packetByteBuf) -> machineEnum.getContainerFactory()
                            .create(syncId, playerInventory, packetByteBuf)
                    )
                );
            });
    }

    public static void onInitializeClient() {
        Arrays.stream(IndustryMachineRegister.values())
            .filter(machineEnum -> machineEnum.getEntityFactory() != null)
            .forEach(machineEnum -> HandledScreens.register(
                machineEnum.getScreenHandlerType(),
                machineEnum.getScreenFactory()
            ));
    }

    public String getName() {
        return name;
    }

    public BlockItem getItem() {
        return item;
    }

    public Block getBlock() {
        return block;
    }

    public FabricBlockEntityTypeBuilder.Factory<? extends BlockEntity> getEntityFactory() {
        return entityFactory;
    }

    public ExtendedScreenHandlerType.ExtendedFactory<MachineContainer> getContainerFactory() {
        return containerFactory;
    }


    public HandledScreens.Provider<MachineContainer, MachineScreen<MachineContainer>> getScreenFactory() {
        return screenFactory;
    }

    public BlockEntityType<? extends BlockEntity> getBlockEntityType() {
        return blockEntityType;
    }

    public ExtendedScreenHandlerType<? extends MachineContainer> getScreenHandlerType() {
        return screenHandlerType;
    }
}
